package com.pingpang.netty.server;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ThreadPoolExecutor;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import com.pingpang.common.annotation.RpcService;
import com.pingpang.common.bean.RequestBean;
import com.pingpang.common.bean.ResponseBean;
import com.pingpang.common.code.CodeUtilEnum;
import com.pingpang.common.code.MessageCodecUtil;
import com.pingpang.common.code.NettyDecoder;
import com.pingpang.common.code.NettyEncoder;
import com.pingpang.registry.SeverRegistry;
import com.pingpang.util.ThreadPoolUtil;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.timeout.IdleStateHandler;

@Component
public class NettyServer implements ApplicationContextAware,InitializingBean{

    private static final Logger logger = LoggerFactory.getLogger(NettyServer.class);
    private static final EventLoopGroup bossGroup = new NioEventLoopGroup(1);
    private static final EventLoopGroup workerGroup = new NioEventLoopGroup(4);
    private static final EventLoopGroup businessGroup = new NioEventLoopGroup(8);
    //序列化反序列化工具
    private static final MessageCodecUtil codeUtil=CodeUtilEnum.PROTOSTUFF.serializer;
    
    private Map<String, Object> serviceMap = new HashMap<>();

    @Value("${rpc.server.address}")
    private String serverAddress;

    @Value("${rpc.server.coreSize}")
    private int coreSize;
    
    @Value("${rpc.server.maxSize}")
    private int maxSize;
    
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {

        Map<String, Object> beans = applicationContext.getBeansWithAnnotation(RpcService.class);
        for(Object serviceBean:beans.values()){

            Class<?> clazz = serviceBean.getClass();

            Class<?>[] interfaces = clazz.getInterfaces();
            
            if(null==interfaces || interfaces.length==0) {
            	logger.info("{}必须实现接口",clazz.getName());
            	throw new RuntimeException(clazz.getName()+"必须实现接口");
            }
            
            for (Class<?> inter : interfaces){
                String interfaceName = inter.getName();
                RpcService service=serviceBean.getClass().getAnnotation(RpcService.class);
                String version=service.version();
                if(null!=version && !"".equals(version.trim())) {
                	interfaceName+="#".concat(version);
                }
                logger.info("加载服务类: {}", interfaceName);
                serviceMap.put(interfaceName, serviceBean);
            }
        }
        logger.info("已加载全部服务接口:{}", serviceMap.keySet());
        
        logger.info("服务注册到zookeeper开始");
        SeverRegistry sr=SeverRegistry.getServerRegistry();
        sr.registAddServer(serviceMap.keySet(), serverAddress);
        //sr.close();//临时节点一直有效
        logger.info("服务注册到zookeeper结束");
        
    }

    public void afterPropertiesSet() throws Exception {
        start();
    }

    public void start(){
    	if(0==this.coreSize){
    		this.coreSize=60;
    	}
    	
    	if(0==this.maxSize) {
    		this.maxSize=300;
    	}
    	final ThreadPoolExecutor threadPool=ThreadPoolUtil.getThreadPool(coreSize, maxSize);
    	final NettyServerHandler handler = new NettyServerHandler(serviceMap,threadPool);
        new Thread(() -> {
            try {
                ServerBootstrap bootstrap = new ServerBootstrap();
                bootstrap.group(bossGroup,workerGroup).
                        channel(NioServerSocketChannel.class).
                        option(ChannelOption.SO_BACKLOG,1024).
                        childOption(ChannelOption.SO_KEEPALIVE,true).
                        childOption(ChannelOption.TCP_NODELAY,true).
                        childHandler(new ChannelInitializer<SocketChannel>() {
                            //创建NIOSocketChannel成功后，在进行初始化时，将它的ChannelHandler设置到ChannelPipeline中，用于处理网络IO事件
                            protected void initChannel(SocketChannel channel) throws Exception {
                                ChannelPipeline pipeline = channel.pipeline();
                                pipeline.addLast(new IdleStateHandler(0, 0, 60));
                                pipeline.addLast(new NettyDecoder(RequestBean.class, codeUtil));
                                pipeline.addLast(new NettyEncoder(ResponseBean.class, codeUtil));
                                pipeline.addLast(businessGroup,handler);
                            }
                        });

                String[] array = serverAddress.split(":");
                String host = array[0];
                int port = Integer.parseInt(array[1]);
                ChannelFuture cf = bootstrap.bind(host,port).sync();
                logger.info("RPC 服务器启动.监听端口:"+port);
                //等待服务端监听端口关闭
                cf.channel().closeFuture().sync();
            } catch (Exception e) {
                logger.error("服务端启动失败!", e);
                bossGroup.shutdownGracefully();
                workerGroup.shutdownGracefully();
            }
        }).start();
    }
}
